home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Programmer Disk
/
The Programmer Disk (Microforum).iso
/
xpro
/
c3
/
pro24
/
mpu.c
< prev
next >
Wrap
C/C++ Source or Header
|
1986-08-07
|
34KB
|
1,147 lines
/*****************************************************************************
* Change Log
* Date | Change
*-----------+-----------------------------------------------------------------
* 31-Dec-85 | Created changelog
* 31-Dec-85 | Add c:\ to include directives
* 31-Dec-85 | Call setctl/clrctl to deal with ctrl-break intercept. Note that
* | clrctl can be called several times, as might be the case during
* | a ctrl-break abort
* 1-Jan-86 | <rgd/jmn> Added keyloud
* | Added initialized flag
* 21-Jan-86 | Abort l_restuntil if Ctrl-C or Ctrl-Break is typed
* 21-Jan-86 | Took out mpu_unknown, replaced with mpu_error_check
* 3-Feb-86 | Fixed MIDI_PROGRAM and MIDI_CHANNEL to subtract one to account
* | for MIDI encodings, e.g. channel 1 is represented by 0
* 8-Feb-86 | Added midi_exclusive to turn on/off flag in mpu-401.
* | Added midi_buffer function.
* 18-Feb-86 | Changed midi_exclusive to exclusive.
* | Added midi_exclusive to send an exclusive message.
* 19-Feb-86 | Changed MC_SEND_... to MC_SND_... to avoid name clash
* 23-Feb-86 | Added midi_control, midi_bend, midi_touch routines
* 24-Feb-86 | Added call to init_asm from musicinit.
* 26-Mar-86 | Added midi_cont call to enable continuous control messages
* 18-Apr-86 | Cleaned up documentation, renamed midi_control to midi_ctrl
* 2-Jun-86 | Added tuning definition code.
* 30-Jun-86 | Added switch to use timdif.asm instead of MPU-401 for timing
* 18-Jul-86 | Added DEBUG switch in parallel with DEBUG in AINTR.ASM
* 29-Jul-86 | Changed trace flag checking to first initialization only.
* 5-Aug-86 | Minor changes for Lattice C Version 3.00
*****************************************************************************/
/*
* Driver for MPU-401
*
* This module initializes things and then issues commands to the
* MPU-401 in response to higher-level operations like note( , , ).
* Unlike examples in the MPU-401 manual, these routines do not wait
* for Acks by polling the MPU-401. Instead, Acks and all other
* input are handled by the interrupt routine (see aintr.asm), which
* sets a flag whenever it sees an Ack. Therefore, these routines
* just poll the Ack flag rather than directly reading the MPU-401.
* This avoids the problem of the MPU-401 having data queued in front
* of the Ack message.
*/
/*
* Interface Specifications for aintr.asm to mpu.c:
*
* aintr.asm is an interrupt handler that also parses midi data and
* queues it for receipt by mpu.c. In this way, interrupts are hidden
* from C programmers and yet the mpu-401 never hangs trying to deliver
* data because the interrupt routine is always ready to read data.
*
* Data is parsed by aintr.asm in order to know how much data to read.
* To avoid reparsing at the C level, the data is buffered in a
* queue of messages rather than a simple byte stream. Each message
* is a 4-byte block. The first byte is a status byte and the following
* bytes (one or two) are data bytes.
*
* Note that timing is not specified. Later, we may add a parallel
* buffer to store 4-byte timing blocks if it seems necessary.
*
* System exclusive messages are handled by copying the midi exclusive
* data into a separate buffer provided by the application program
* through the midi_buffer call.
*
*/
/* define DEBUG to enable some extra debugging */
/* #define DEBUG 1 */
/* define TIMDIF to use the timdif.asm module */
/* #define TIMDIF 1 */
#include "cext.h"
#include "stdio.h"
#include "atxt.h"
#include "mpu.h"
#include "midicode.h"
#include "cmdline.h"
#include "userio.h"
#include "pitch.h"
#include "cintr.h"
#ifdef TIMDIF
#include "timdif.h"
#endif
#define BREAKTEST if (CBREAK) { /* Ctrl-C or Ctrl-Break handler */ \
printf("Exiting.\n");\
musicterm();\
exit(1);\
}
#define num_voices 16
/****************************************************************************
*
* MPU 401 commands
*
****************************************************************************/
#define MC_RESET 0xff
#define MC_SET_TEMPO 0xe0
#define MC_TIMEBASE_192 0xc8
#define MC_START_RECORDING 0x22
#define MC_RECORD_COUNTER 0xab
#define MC_ON_METRONOME 0x85
#define MC_VERSION 0xac
#define MC_REVISION 0xad
#define MC_NO_MEASURE_END 0x8c
#define MC_SND_MIDI 0xd0
#define MC_SND_EXCLUSIVE 0xdf
#define MC_EXCLUSIVE 0x96
#define MC_OFF_BEND 0x86
#define MC_ON_BEND 0x87
#define MC_OFF_THRU 0x88
#define MC_ON_THRU 0x89
/****************************************************************************
*
* useful defines and macros
*
****************************************************************************/
#define MD_BEATS_PER_MINUTE 125 /* to get 400 ticks per sec */
#define TICKS_TO_HUNDREDTHS(t) ((t) >> 2)
#define HUNDREDTHS_TO_TICKS(h) ((h) << 2)
#define NONE (-1)
/****************************************************************************
*
* MPU-401 interface to IBM-XT
*
****************************************************************************/
#define DATAPORT 0x330 /* Input data port of MPU 401 */
#define STATPORT 0x331 /* status and request port */
#define COMPORT 0x331 /* Send MPU 401 commands to here */
#define DSR (1<<7) /* This bit of STATPORT, when low,
means the 401 has Data to send */
#define DRR (1<<6) /* This bit of STATPORT, when low,
means the 401 will take commands */
#define ACK 0xfe /* Acknowledgement data code */
#define IRQ 2 /* Interrupt request line of 401 */
#define MAX_ACK_WAIT 1000
/****************************************************************************
*
* exported flags
*
****************************************************************************/
boolean musictrace = false; /* enables printed trace of commands */
boolean miditrace = false; /* enables printed trace of MIDI output */
#define n_t_sw 2
private char *t_switches[n_t_sw] = { "-t", "-trace" };
#define n_m_sw 2
private char *m_switches[n_m_sw] = { "-m", "-miditrace" };
/****************************************************************************
*
* exported variables
*
****************************************************************************/
int keyloud; /* set to velocity of last getkey event */
/****************************************************************************
*
* variables shared with aintr.asm
*
****************************************************************************/
int intnest = 0; /* do not touch...read-only except by aintr.asm */
int rd_delay = 0; /* do not touch...read-only except by aintr.asm */
int Unknown = ACK;
int interror = 0; /* reports errors from interrupt handler */
int timeerr = 0; /* reports timeout errors */
#ifdef DEBUG
int intcnt = 0; /* Count of interrupts taken */
int loop_cnt, loop_max; /* iteration counts */
#endif
long Ticks; /* Number of clock ticks since init */
int Ack; /* True if command has been acknowledged */
char MidiTime, MidiStat = 0x90, Midi1, Midi2, Midi3; /* midi and time */
char exclflag = 0; /* used by aintr, DO NOT TOUCH */
int time_req = 0; /* set to 1 when Ack will be followed by data */
int mpu_result; /* the data following the Ack */
/****************************************************************************
*
* Variable set by BREAK module
*
****************************************************************************/
extern int CBREAK;
/****************************************************************************
*
* variable imported from cintr.c
*
****************************************************************************/
extern int enabled;
/****************************************************************************
*
* local module variables
*
****************************************************************************/
private int initialized = false; /* set by musicinit, cleared by musicterm */
private boolean tune_flag = false; /* set by musicinit, never cleared */
private boolean metroflag = false; /* flag to turn on metronome */
private boolean mpuflag = true; /* true iff mpu401 present */
private int len; /* length of trace string */
private int last_cmd = 0; /* last mpu_command, used by trace */
private int user_scale = false; /* true if user-defined scale */
private int bend[num_voices]; /* current pitch bend on channel */
private pitch_table pit_tab[128]; /* scale definition */
/* "temporary" instrumentation: how long should we wait? */
private int max_ack_wait = 0; /* maintained by mpu_wait */
/****************************************************************************
*
* functions declared in this module
*
****************************************************************************/
private void fixup();
private void mpu_command();
private void mpu_drr_wait();
private int mpu_read();
private void mpu_wait();
private void mpu_write();
private void trace_mpu_command();
private void wfa();
/****************************************************************************
*
* Buffer
* shares data with aintr.asm (the producer)
*
****************************************************************************/
#define BUFFERSIZE 1024
byte buff[BUFFERSIZE]; /* data buffer */
int buffhead = 0; /* buffer pointers */
int bufftail = 0;
/****************************************************************************
*
* System exclusive buffer variables (shared with aintr.asm)
*
****************************************************************************/
byte *xbuff = 0; /* address of the user-supplied buffer */
int xbuffmask; /* mask for circular buffer address calculation */
int xbuffhead = 0; /* buffer pointers */
int xbufftail = 0;
/****************************************************************************
* exclusive
* Inputs:
* boolean onflag -- set to true to receive midi exclusive data
* Effect:
* Tells MPU401 to read exclusive messages into buffer
****************************************************************************/
void exclusive(onflag)
boolean onflag; /* on or off? */
{
if (!initialized) fixup();
if (musictrace)
printf("exclusive: %d\n", onflag);
mpu_command(MC_EXCLUSIVE | (onflag ? 1 : 0));
}
/****************************************************************************
* fixup
* Effect:
* Print error message and call musicinit
****************************************************************************/
private void fixup()
{
printf("You forgot to call musicinit. I'll do it for you.\n");
musicinit();
}
/****************************************************************************
* getbuf
* Inputs:
* boolean waitflag: true if routine should wait for data
* byte * p: Pointer to data destination
* Result: boolean
* true if data was written to *p
* false if data not written to *p
* Effect:
* copies data from buffer to *p
* will wait for buffer to become nonempty if waitflag is true
****************************************************************************/
boolean getbuf(waitflag, p)
boolean waitflag; /* true if routine should wait for data */
byte *p; /* pointer to data destination */
{
/* register int head;*/
if (!initialized) fixup();
if (waitflag) while (buffhead == bufftail) /* wait */ ;
else if (buffhead == bufftail) return false;
*(long *)p = *(long *)(buff+buffhead);
buffhead += 4;
if (buffhead >= BUFFERSIZE) buffhead = 0;
/* the previous three lines are an optimization of:
* head = buffhead;
* *p++ = buff[head++];
* *p++ = buff[head++];
* *p++ = buff[head++];
* head++;
*
* if (head >= BUFFERSIZE) head = 0;
* buffhead = head;
*/
return true;
}
/****************************************************************************
* getkey
* Inputs:
* boolean waitflag: true if wait until key depression, false if
* return immediately
* Result: int
* key number of key which has been depressed
* It returns -1 if waitflag is false and no key has been pressed
* If waitflag is true this routine will block until a key is pressed
* Effect:
* reads a key
****************************************************************************/
int getkey(waitflag)
{
byte msg[4];
int k;
if (!initialized) fixup();
while (true) { /* process data until you find a note */
/* look for data and exit if none found */
/* NOTE: waitflag will force waiting until data arrives */
if (!getbuf(waitflag, msg)) { /* nothing there */
k = -1;
break;
} else if ((msg[0] & MIDI_CODE_MASK) == MIDI_ON_NOTE) {
if (msg[2] == 0) { /* velocity 0 -> note off */
keyloud = 0;
k = (msg[1]-12) + 128;
} else {
keyloud = msg[2];
k = (msg[1]-12);
}
break;
} else if ((msg[0] & MIDI_CODE_MASK) == MIDI_OFF_NOTE) {
keyloud = 0;
k = (msg[1]-12) + 128;
break;
}
}
if (musictrace) {
if (k != -1) printf("getkey got %d\n", k);
}
return k;
}
/****************************************************************************
* gettime
* Result: long
* current timestamp from MPU-401
* Return the time in 100ths of seconds since the last call to
* musicinit or timereset
* Effect:
* Reads the MPU-401 time
****************************************************************************/
/* just to make sure we're using the timdif module: */
long gettime()
{
BREAKTEST /* abort if user typed Ctrl Break */
if (!initialized) fixup();
#ifndef TIMDIF
time_req = 1; /* tell aintr.asm to read extra byte */
mpu_command(MC_RECORD_COUNTER); /* Read counter */
return TICKS_TO_HUNDREDTHS(Ticks);
#else
return ibm_time();
#endif
}
/****************************************************************************
* l_rest
* Inputs:
* long time: Amount of time to rest
* Effect:
* Waits until the amount of time specified has lapsed
****************************************************************************/
void l_rest(time)
long time;
{
if (!initialized) fixup();
l_restuntil(time + gettime());
}
/****************************************************************************
* l_restuntil
* Inputs:
* long time: Event time to rest until
* Effect:
* Waits until the specified time has been reached (absolute time)
****************************************************************************/
void l_restuntil(time)
long time;
{
while(time > gettime()) ;
}
/****************************************************************************
* metronome
* Inputs:
* int onflag: true or false
* Effect:
* enables (true) or disables (false) MPU-401 metronome function.
* must be called before musicinit
****************************************************************************/
void metronome(onflag)
int onflag;
{
metroflag = onflag;
}
/****************************************************************************
* midi_bend
* Inputs:
* int channel: midi channel on which to send data
* int value: pitch bend value
* Effect:
* Sends a midi pitch bend message
****************************************************************************/
void midi_bend(channel, value)
int channel, value;
{
if (!initialized) fixup();
if (musictrace)
printf("midi_bend: ch %d, val %d\n", channel, value);
bend[MIDI_CHANNEL(channel)] = value;
mpu_command(MC_SND_MIDI);
mpu_write(MIDI_BEND | MIDI_CHANNEL(channel));
mpu_write(MIDI_DATA(value));
mpu_write(MIDI_DATA(value>>7));
}
/****************************************************************************
* midi_buffer
* Inputs:
* byte * buffer: the buffer address
* int size: number of bytes in buffer
* Returns:
* false if size is less than 16 or buffer is NULL, otherwise true
* Effect:
* tells interrupt routine to store system exclusive messages in
* buffer. The largest power of 2 bytes less than size will be
* used. xbuffhead and xbufftail will be initialized to zero,
* and xbufftail will be one greater than the index of the last
* system exclusive byte read from mpu401.
****************************************************************************/
int midi_buffer(buffer, size)
byte *buffer;
int size;
{
int mask;
mask = 16 - 1;
if (size < 16 || buffer == NULL) return false;
while (mask < size && mask > 0) mask = (mask << 1) + 1;
xbuff = NULL; /* turn off buffering */
xbuffmask = mask >> 1;
xbuffhead = xbufftail = 0;
xbuff = buffer; /* set buffer, turn on buffering */
return true;
}
/****************************************************************************
* midi_cont
* Inputs:
* boolean onflag: true or false
* Effect:
* enables (true) or disables (false) continuous control info from
* MPU-401 to host.
****************************************************************************/
void midi_cont(onflag)
boolean onflag;
{
if (onflag) mpu_command(MC_ON_BEND);
else mpu_command(MC_OFF_BEND);
}
/****************************************************************************
* midi_ctrl
* Inputs:
* int channel: midi channel on which to send data
* int control: control number
* int value: control value
* Effect:
* Sends a midi control change message
****************************************************************************/
void midi_ctrl(channel, control, value)
int channel, control, value;
{
if (!initialized) fixup();
if (musictrace)
printf("midi_ctrl: ch %d, ctrl %d, val %d\n",
channel, control, value);
mpu_command(MC_SND_MIDI);
mpu_write(MIDI_CTRL | MIDI_CHANNEL(channel));
mpu_write(MIDI_DATA(control));
mpu_write(MIDI_DATA(value));
}
/****************************************************************************
* midi_exclusive
* Inputs:
* byte * msg: pointer to a midi exclusive message, terminated by 0xF7
* Effect:
* Sends a midi exclusive message
****************************************************************************/
void midi_exclusive(msg)
byte *msg; /* the data to be sent */
{
int i; /* can DX7 keep up? */
/* if user mistakenly called midi_exclusive instead of exclusive,
* the argument will be true or false, both of which are highly
* unlikely valid arguments for midi_exclusive:
*/
if (msg == (byte *) false || msg == (byte *) true) {
printf("midi_exclusive: invalid argument %d.\n", (int) msg);
if (initialized) musicterm();
exit(1);
}
if (!initialized) fixup();
if (musictrace) printf("midi_exclusive\n");
mpu_command(MC_SND_EXCLUSIVE);
while (*msg != MIDI_EOX) {
mpu_write(*msg);
msg++;
/* This is a delay loop. Without it, your DX7 will crash. */
for (i = (atxt() == ISAT ? 4 : 2); i > 0; i--) {
BREAKTEST
}
}
mpu_write(MIDI_EOX);
}
/****************************************************************************
* midi_note
* Inputs:
* int channel: midi channel on which to send data
* int pitch: midi pitch code
* int velocity: velocity with which to sound it (0=> release)
* Effect:
* Sends a midi note-play request out
****************************************************************************/
void midi_note(channel, pitch, velocity)
int channel, pitch, velocity;
{
if (!initialized) fixup();
if (musictrace)
printf("midi_note: ch %d, key %d, vel %d\n",
channel, pitch, velocity);
if (user_scale) {
/* check for correct pitch bend */
if ((pit_tab[pitch+12].pbend != bend[MIDI_CHANNEL(channel)]) &&
(velocity != 0)) {
midi_bend(channel, pit_tab[pitch+12].pbend);
bend[channel] = pit_tab[pitch+12].pbend;
}
pitch = pit_tab[pitch+12].ppitch;
}
mpu_command(MC_SND_MIDI);
mpu_write(MIDI_ON_NOTE | MIDI_CHANNEL(channel));
mpu_write(MIDI_DATA(12 + pitch)); /* cmu standard to midi standard */
mpu_write(MIDI_DATA(velocity));
}
/****************************************************************************
* midi_program
* Inputs:
* int channel: Channel on which to send midi program change request
* int program: Program number to send (decremented by 1 before
* being sent as midi data)
* Effect:
* Sends a program change request out the channel
****************************************************************************/
void midi_program(channel, program)
int channel; /* midi channel */
int program; /* the program number */
{
if (!initialized) fixup();
if (musictrace)
printf("midi_program: ch %d, prog %d\n",
channel, program);
mpu_command(MC_SND_MIDI);
mpu_write(MIDI_CH_PROGRAM | MIDI_CHANNEL(channel));
mpu_write(MIDI_PROGRAM(program));
}
/****************************************************************************
* midi_thru
* Inputs:
* boolean onflag: true or false
* Effect:
* enables (true) or disables (false) midi thru info from
* MPU-401 to host. (Default is set; reset with cmdline -block.)
****************************************************************************/
void midi_thru(onflag)
boolean onflag;
{
if (onflag) mpu_command(MC_ON_THRU);
else mpu_command(MC_OFF_THRU);
}
/****************************************************************************
* midi_touch
* Inputs:
* int channel: midi channel on which to send data
* int value: control value
* Effect:
* Sends a midi after touch message
****************************************************************************/
void midi_touch(channel, value)
int channel, value;
{
if (!initialized) fixup();
if (musictrace)
printf("midi_touch: ch %d, val %d\n", channel, value);
mpu_command(MC_SND_MIDI);
mpu_write(MIDI_TOUCH | MIDI_CHANNEL(channel));
mpu_write(MIDI_DATA(value));
}
/****************************************************************************
* mpu_command
* Inputs:
* int c: Character to write to MPU-401 command port
* Effect:
* Writes the data to the MPU-401 command port
****************************************************************************/
private void mpu_command(c)
int c;
{
if (!mpuflag) { /* simulated */
trace_mpu_command(c);
} else { /* real */
if (miditrace) trace_mpu_command(c);
mpu_drr_wait();
Ack = 0;
outp(COMPORT, c);
if (enabled) mpu_wait();
else wfa();
}
}
/****************************************************************************
* mpu_drr_wait
* Effect:
* Waits until the MPU-401 is ready to receive data
****************************************************************************/
#define MAX_TRIES 2000
private void mpu_drr_wait()
{
int i;
if (!mpuflag) return; /* always ready if not there! */
for (i = 0; i < MAX_TRIES; i++)
if ((inp(STATPORT) & DRR) == 0) break;
#ifdef DEBUG
if (i == MAX_TRIES)
printf("mpu-401 not ready to receive; intcnt=%d\n",intcnt);
#endif
}
/****************************************************************************
* mpu_error_check
* Effect:
* Reports any errors originating in the interrupt handler
****************************************************************************/
void mpu_error_check()
{
if (Unknown != ACK) {
printf("Unknown command: %x\n", Unknown);
Unknown = ACK;
}
if (interror != 0) {
char *cause;
switch (interror) {
case NESTERR: cause = "nested interrupts"; break;
case BUFFERR: cause = "buffer overflow"; break;
case CMDERR: cause = "unknown command"; break;
default: cause = ""; break;
}
printf("interror: %s\n", cause);
if (*cause == NULL) printf("%d\n", interror);
interror = 0;
}
if (timeerr != 0) {
if (timeerr == TIMEOUT) printf("timeerr: timeout error\n");
else printf("timeerr = %d\n", timeerr);
timeerr = 0;
}
}
/****************************************************************************
* mpu_read
* Result: int
* character read from MPU-401
* Effect:
* Reads the MPU-401
****************************************************************************/
private int mpu_read()
{
int delay;
for (delay = 0; delay < 2000; delay++) {
if ((inp(STATPORT) & DSR) == 0)
return inp(DATAPORT);
}
#ifdef DEBUG
printf("mpu_read: DSR never went low, returning 0, intcnt=%d\n",intcnt);
#endif
return 0;
}
/****************************************************************************
* mpu_wait
* Effect:
* Called when interrupts are enabled. Polls the 'Ack' flag, which is
* set by the interrupt handler. If more than MAX_ACK_WAIT iterations
* occur without 'Ack' being set, issues an error message.
* Ack is cleared when it is detected.
****************************************************************************/
private void mpu_wait()
{
int ackcnt; /* delay counter */
if (!mpuflag) return;
for (ackcnt = 0; ackcnt < MAX_ACK_WAIT; ackcnt++) {
if (Ack) {
if (max_ack_wait < ackcnt) max_ack_wait = ackcnt;
Ack = 0;
return;
}
}
#ifdef DEBUG
printf("mpu_wait: No ack; incnt = %d\n",intcnt);
#endif
}
/****************************************************************************
* mpu_write
* Inputs:
* int c: Character to write to MPU-401 data port
* Effect:
* Writes the data to the MPU-401 data port
****************************************************************************/
private void mpu_write(c)
int c;
{
if (!mpuflag) { /* simulate */
printf("%02x",c);
len += 2;
} else { /* real */
if (miditrace) { /* trace */
printf("%02x",c);
len += 2;
} /* trace */
mpu_drr_wait();
outp(DATAPORT, c);
}
}
/****************************************************************************
* mpuexists
* Inputs:
* boolean flag: true or false
* Effect:
* if argument is false, indicates no mpu is on the machine, so
* simulate mpu-401 (for debugging only)
****************************************************************************/
void mpuexists(flag)
boolean flag;
{
mpuflag = flag;
}
/*****************************************************************
* set_pitch_default
*****************************************************************/
private void set_pitch_default()
{
int i;
for (i = 0; i < 128; i++) {
pit_tab[i].pbend = 8192;
pit_tab[i].ppitch = i;
}
}
/*****************************************************************
* read_tuning
*****************************************************************/
void read_tuning(filename)
char *filename;
{
int index, pit, lineno = 0;
float bend;
FILE *fpp;
user_scale = true;
set_pitch_default();
fpp = fileopen(filename, "tun", "r", "Tuning definition file");
while ((fscanf(fpp, "%d %d %f\n", &index, &pit, &bend) > 2) &&
(lineno < 128)) {
lineno++;
if (index >= -12 && index <= 115) {
pit_tab[index+12].pbend = (int)(8192 * bend/100 + 8192);
pit_tab[index+12].ppitch = pit;
}
}
}
/****************************************************************************
* musicinit
* Effect:
* Initialize the mpu 401 device driver
* Initialize mpu 401
* Reset 401, change defaults
* Set up interrupts
* Start up mpu record clock
****************************************************************************/
void musicinit()
{
int version, revision;
int i;
char *filename;
if (!tune_flag) { /* do this code only once */
miditrace = (cl_nswitch(m_switches, n_m_sw) != NULL);
musictrace = (cl_nswitch(t_switches, n_t_sw) != NULL);
tune_flag = true;
filename = cl_option("-tune");
if (filename != NULL) {
read_tuning(filename);
}
intr_init();
}
last_cmd = 0;
#ifdef TIMDIF
if (!initialized) settime();
#endif
initialized = true;
intr_disable(IRQ); /* Turn off 401 interrupts */
for (i = 0; i < 100; i++) { /* flush out buffer, ignore DSR */
inp(DATAPORT);
}
mpu_command(MC_RESET); /* Reset the device */
mpu_command(MC_VERSION);
version = mpu_read();
mpu_command(MC_REVISION);
revision = mpu_read();
mpu_command(MC_SET_TEMPO); /* Set tempo and timebase to get */
mpu_write(MD_BEATS_PER_MINUTE); /* 400 ticks per second */
mpu_command(MC_TIMEBASE_192);
if (metroflag)
mpu_command(MC_ON_METRONOME); /* Just for debugging */
mpu_command(MC_NO_MEASURE_END); /* Don't want measure end bytes */
init_asm(); /* Do any asm init needed(aintr.asm)*/
intr_routine(IRQ); /* Set up vector */
CBREAK = false;
setctl(); /* Set up ctrl-break intercept */
#ifdef DEBUG
loop_max = 0;
#endif
intr_enable(IRQ); /* allow 401 interrupts */
if (user_scale) {
for (i = 0; i < num_voices; i++) {
midi_bend(i, 8192);
bend[i] = 8192;
}
}
midi_thru(!(cl_switch("-block"))); /* set MIDI thru on MPU-401 */
timereset(); /* Reset clock */
}
/****************************************************************************
* musicterm
* Effect:
* Cleans up; disables MPU-401 interrupts; resets MIDI devices
****************************************************************************/
void musicterm()
{
if (initialized) {
#ifdef TIMDIF
cletime();
#endif
intr_disable(IRQ); /* No more 401 interrupts */
#ifdef DEBUG
printf("loop_max is %d\n", loop_max);
#endif
clrctl(); /* reset ctrl-break handler */
intr_cleanup(IRQ); /* Restore default vector */
mpu_command(MC_RESET); /* Reset the device */
initialized = false;
}
/* printf("maximum successful wait for ack: %d\n", max_ack_wait);*/
}
/****************************************************************************
* random
* Inputs:
* int lo: Lower limit of value
* int hi: Upper limit of value
* Result: int
* random number (lo <= result <= hi)
****************************************************************************/
private long seed = 1534781;
int random(lo, hi)
int lo, hi;
{
seed *= 13;
seed += 1874351;
return (int) (lo +
(((hi + 1 - lo) * ((0x00ffff00 & seed) >> 8)) >> 16));
}
/****************************************************************************
* timereset
* Effect:
* Resets the time on the MPU-401. Ticks is reset to 0
****************************************************************************/
void timereset()
{
if (!initialized) fixup();
if (musictrace) printf("timereset()\n");
Ticks = 0; /* Reset clock */
mpu_command(MC_START_RECORDING); /* Starts up clock */
#ifdef TIMDIF
cletime();
settime();
#endif
}
/****************************************************************************
* trace
* Inputs:
* boolean flag: true for trace on
* Effect:
* turns tracing on (flag == true) or off (flag == false)
****************************************************************************/
void trace(flag)
boolean flag;
{
musictrace = flag;
}
/****************************************************************************
* tracemidi
* Inputs:
* boolean flag: true for trace on
* Effect:
* turns midi tracing on (flag == true) or off (flag == false)
****************************************************************************/
void tracemidi(flag)
boolean flag;
{
miditrace = flag;
}
/****************************************************************************
* trace_mpu_command
* Inputs:
* int c: Command
* Effect:
* Writes command to stdout
****************************************************************************/
private void trace_mpu_command(c)
int c;
{
char * p;
char buf[10];
switch(c) { /* decode */
case MC_RESET:
p = " RESET:";
break;
case MC_ON_METRONOME:
p =" MET-ON:";
break;
case MC_SET_TEMPO:
p =" TEMPO:";
break;
case MC_TIMEBASE_192:
p =" TIME-192:";
break;
case MC_START_RECORDING:
p =" REC-ON:";
break;
case MC_VERSION:
p =" VERSION:";
break;
case MC_REVISION:
p =" REVISION:";
break;
case MC_NO_MEASURE_END:
p =" NO-MEAS-END:";
break;
case MC_SND_MIDI:
p =" MIDI:";
break;
case MC_RECORD_COUNTER:
if (last_cmd == c) p = "#";
else p = " COUNTER:";
break;
case MC_ON_BEND:
p = "Bender:On";
break;
case MC_OFF_BEND:
p = "Bender:Off";
break;
case MC_ON_THRU:
p = "Thru:On";
break;
case MC_OFF_THRU:
p = "Thru:Off";
break;
default:sprintf(buf," %02x",c);
p = buf;
break;
} /* decode */
last_cmd = c;
if (len + strlen(p) > 70) { /* overflow */
printf("\n");
len = 0;
} /* overflow */
len += strlen(p);
printf("%s",p);
}
/****************************************************************************
* wfa
* Effect:
* Waits for an acknowledgement from the MPU-401. Will not wait more
* than MAX_ACK_WAIT iterations thru its loop.
* Conditions:
* Called only if interrupts are not enabled, and the MPU-401 is
* being polled
****************************************************************************/
private void wfa()
{
int ackcnt;
int x;
if (!mpuflag) return;
for (ackcnt = 0; ackcnt < MAX_ACK_WAIT; ackcnt++)
if ((x = mpu_read()) == ACK) break;
#ifdef DEBUG
else printf("wfa: got %x; intcnt = %d\n", x, intcnt);
#endif
}